# -*- coding: cp866 -*-
import wsgiref.util
import cgi
import urlparse
import datetime
import time
import pdb
import base64
import Cookie

import MSConfig; c = MSConfig.config()
import MSLogger; log = MSLogger.get('wsgi_server')
import MSSafeinsideLib as lib
import MSDatabase
import MSWSGIPatterns as w
import MSObject
import MSObjects
import MSUser
import MSUsers
import MSDatetime
import ReaderMSAPI
import ReaderMSDB
import MSEvent

class Helpers:
    @staticmethod
    def checkName(name):
        if w.checkName(name): return True
        if type(name) != type(str()): name = 'bad_type'
        log.debug('wrong name %s', name)
        return False

class MethodHandler:
    #this class should not have any fields until methods are invoked directly by http request (using getattr)
    #otherwise internal fields could be accessed too
    def __init__(self): return

    def object_registrate(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oid = form.getvalue('obj_id')
        if not Helpers.checkName(oid): return ['Ошибка - недопустимый идентификатор']
        mac = form.getvalue('obj_mac')
        if not Helpers.checkName(mac): return ['Ошибка - недопустимый MAC']

        reader = lib.MSDBReader(c.get('msdb','host'),
                                c.get('msdb','port'),
                                c.get('msdb','dbname'),
                                c.get('msdb','user'),
                                c.get('msdb','pwd'))
        reader.connect()
        try:
            res = lib.MSObjects.registrateObjectInMSDB(reader.connection, 'ms.online.normal_objects.cfg', oid, mac)
        except Exception,err:
            return ['Ошибка - не удалось зарегистрировать устройство: ' + str(err)]
        return ['Устройство успешно зарегистрировано: ' + str(res)]

    def admin_mode(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])

        db = MSDatabase.getInstance()
        db.users.lock.acquire()
        ret = ['<h2>Список объектов</h2>']
        t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')

        ret += t.open([('login', {'class':'header'}),
                       ('email',  {'class':'yellow header'}),
                       ('admin',   {'class':'green header'}),
                      ])

        try:
            for k,u in db.users.objs.iteritems():
                login = 'N/A'
                if u.storage.meta.has_key('email'): login = u.storage.meta['email']
                email = 'N/A'
                if u.storage.meta.has_key('realmail'): email = u.storage.meta['realmail']
                admin = 'N/A'
                if u.storage.meta.has_key('isCar') and (u.storage.meta['isCar'] == True):
                    admin = '<form action="/method/admin_interface_change" method="post">'\
                            '<input class="span8" id="user_email" name="user_email" size="30" type="hidden" value="%s">'\
                            '<input class="span8" id="user_isCar" name="user_isCar" size="30" type="hidden" value="False">'\
                            '<button class="btn primary" type="submit">Использовать интерфейс "Личный кабинет"</button></form>' % str(login)
                else:
                    admin = '<form action="/method/admin_interface_change" method="post">'\
                            '<input class="span8" id="user_email" name="user_email" size="30" type="hidden" value="%s">'\
                            '<input class="span8" id="user_isCar" name="user_isCar" size="30" type="hidden" value="True">'\
                            '<button class="btn primary" type="submit">Использовать интерфейс "Car-Online"</button></form>' % str(login)
                ret += t.add((  str(login), 
                                str(email),
                                str(admin) ))
        finally:
            ret += t.close()
            db.users.lock.release()
        return ret

    def admin_interface_change(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        login   = form.getvalue('user_email')
        if not Helpers.checkName(login):
            return ['failed - wrong login']
        isCar   = form.getvalue('user_isCar')
        if isCar != 'True' and isCar != 'False':
            return ['failed - wrong interface']

        uid = MSUser.hash(login)
        u   = MSDatabase.getInstance().users.find(uid)
        if u != None:
            descr = ''
            if isCar == 'True': 
                u.storage.meta['isCar'] = True
                descr = 'Успешно выбран интерфейс "Car-Online"'
            else: 
                u.storage.meta['isCar'] = False
                descr = 'Успешно выбран интерфейс "Личный кабинет"'
            u.flush()
            return [descr]

        return ['failed - no such user']


    def login(self, environ, start_response, cookie):
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        email   = form.getvalue('login')
        if not Helpers.checkName(email):
            w.Common.putHeader(start_response, 200, [('html', 'text')])
            return ['failed']
        pwd     = form.getvalue('password')
        if not Helpers.checkName(pwd): 
            w.Common.putHeader(start_response, 200, [('html', 'text')])
            return ['failed']

        uid         = MSUser.hash(email)
        user        = MSDatabase.getInstance().users.find(uid)
        ret         = ['Выполнен вход %s' % (email)]
        ret         += ['<form action="method/user_card" method="post">']
        ret         += ['<button class="btn primary" type="submit">Продолжить</button></form>']
        if user == None:
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))
            reader.connect()
            cursor = reader.connection.cursor()
            query = "SELECT login,password,defaultDevice FROM _users WHERE login='%s'" % (email)
            cursor.execute(query)
            res = cursor.fetchall()
            if len(res) != 0 and res[0][1] == pwd:
                user = MSUser.MSUser(c.get('main','dbpath'), uid)
                user.open()
                user.storage.meta['email'] = email
                user.storage.meta['pwd'] = MSUser.hash(pwd)
                user.storage.meta['msId'] = str(res[0][2])
                user.flush()
                MSDatabase.getInstance().users.add(user)
                ret = ['Выполнен вход %s<br>' % (email)]
                ret += ['Добро пожаловать в AN-Direct!<br>']
                ret += ['Необходимо заполнить информацию по устройству<br>']
                ret += ['<form action="/method/user_card_modify" method="post">']
                ret += ['<button class="btn primary" type="submit">Продолжить</button></form>']

        if user != None and (MSUser.hash(pwd) == user.storage.meta['pwd']):
            cookie = user.getCookie()
            log.info('logged in: %s,%s', email, cookie.output(header=''))
            w.Common.putHeader(start_response, 200, [('html', 'text'), ('Set-Cookie', cookie.output(header=''))])
            if user.storage.meta.has_key('isCar') and (user.storage.meta['isCar'] == True):
                redirect = '/html/contact.html?login=%s&password=%s' % (str(email), str(pwd))
                return (ret, redirect)
            else:
                redirect = '/method/user_card'
                return (ret, redirect)

        w.Common.putHeader(start_response, 200, [('html', 'text')])
        return ['failed']

    def logoff(self, environ, start_response, cookie):
        cookie = MSUser.cookie(environ)
        result = MSUser.Cookie.SimpleCookie()
        if MSUser.isValid(cookie):
            if cookie.has_key('id'):
                result['id'] = str(cookie['id'].value)
                result['id']['expires'] = -1
            if cookie.has_key('session'):
                result['session'] = str(cookie['session'].value)
                result['session']['expires'] = -1
            log.info('logged off: was (%s) set(%s)', cookie.output(header=''), result.output(header=''))
        w.Common.putHeader(start_response, 200, [('html', 'text'), ('Set-Cookie', result.output(header=''))])
        return ['logged off']

    def register(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])

        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        email   = form.getvalue('user_email')
        if not Helpers.checkName(email): return ['Ошибка - недопустимый email']
        pwd     = form.getvalue('user_pwd')
        if not Helpers.checkName(pwd): return ['Ошибка - недопустимый пароль']
        pwd2    = form.getvalue('user_pwd2')
        if not Helpers.checkName(pwd2): return ['Ошибка - недопустимый пароль']
        if MSUser.hash(pwd) != MSUser.hash(pwd2): return ['Ошибка - пароли несовпадают']

        cardtype    = form.getvalue('user_cardtype')
        realname    = form.getvalue('user_realname')
        phone       = form.getvalue('user_phone')
        civility    = form.getvalue('user_civility')
        startdate   = form.getvalue('user_startdate')
        stopdate    = form.getvalue('user_stopdate')
        photolink   = form.getvalue('user_photolink')
        extrainfo   = form.getvalue('user_extrainfo')

        if (not startdate) or (not stopdate): return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']
        try:
            if (not startdate) or (not stopdate): raise RuntimeError('argument missing')
            startdate = datetime.datetime.strptime(startdate, '%d.%m.%Y')
            stopdate  = datetime.datetime.strptime(stopdate, '%d.%m.%Y')
        except:
            return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']

        u = MSUser.MSUser(c.get('main','dbpath'), MSUser.hash(email))
        u.open()
        if u.storage.txn > 0: return ['failed - already exists']
        u.storage.meta['email'] = email
        u.storage.meta['pwd'] = MSUser.hash(pwd)
        if cardtype:    u.storage.meta['cardtype'] = cardtype
        if realname:    u.storage.meta['realname'] = realname
        if phone:       u.storage.meta['phone'] = phone
        if civility:    u.storage.meta['civility'] = civility
        if startdate:   u.storage.meta['startdate'] = startdate
        if stopdate:    u.storage.meta['stopdate'] = stopdate
        if photolink:   u.storage.meta['photolink'] = photolink
        if extrainfo:   u.storage.meta['extrainfo'] = extrainfo
        u.flush()
        MSDatabase.getInstance().users.add(u)
        log.info('registrated user %s as %s', email, MSUser.hash(email))
        return ['success']

    def modify(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        if MSUser.isValid(cookie):
            u = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if u != None and u.validate(cookie):
                cardtype    = form.getvalue('user_cardtype')
                realname    = form.getvalue('user_realname')
                phone       = form.getvalue('user_phone')
                civility    = form.getvalue('user_civility')
                startdate   = form.getvalue('user_startdate')
                stopdate    = form.getvalue('user_stopdate')
                photolink   = form.getvalue('user_photolink')
                extrainfo   = form.getvalue('user_extrainfo')
                mapShow     = form.getvalue('user_displayAtMap')
                mapComment  = form.getvalue('user_mapComment')
                realmail    = form.getvalue('user_realmail')
                alarmHours  = form.getvalue('user_alarmHours')

                if (not startdate) or (not stopdate): return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']
                try:
                    if (not startdate) or (not stopdate): raise RuntimeError('argument missing')
                    startdate = datetime.datetime.strptime(startdate, '%d.%m.%Y')
                    stopdate  = datetime.datetime.strptime(stopdate, '%d.%m.%Y')
                except:
                    return ['Ошибка - введите дату выдачи и оконачания карты Hot-Line в требуемом формате']

                if cardtype:    u.storage.meta['cardtype'] = cardtype
                if realname:    u.storage.meta['realname'] = realname
                if phone:       u.storage.meta['phone'] = phone
                if civility:    u.storage.meta['civility'] = civility
                if startdate:   u.storage.meta['startdate'] = startdate
                if stopdate:    u.storage.meta['stopdate'] = stopdate
                if photolink:   u.storage.meta['photolink'] = photolink
                if extrainfo:   u.storage.meta['extrainfo'] = extrainfo
                if realmail:    u.storage.meta['realmail'] = realmail
                if alarmHours:
                    try:
                        alarmHours = int(alarmHours)
                        u.storage.meta['alarmHours'] = str(alarmHours)
                    except:
                        return ['Ошибка - неверно задано количество часов без выхода на связь - должно быть число']
                if mapShow == 'on':
                    u.storage.meta['mapShow'] = True
                else:
                    u.storage.meta['mapShow'] = False
                if mapComment:
                    try:
                        ts = u'test string'
                        ts += mapComment
                        u.storage.meta['mapComment'] = mapComment
                    except:
                        return ['Ошибка - недопустимые символы в комментарии на карте']
                u.flush()
                if u.storage.meta.has_key('msId'):
                    reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                               c.get('msdb','port'),
                                               c.get('msdb','dbname'),
                                               c.get('msdb','user'),
                                               c.get('msdb','pwd'))
                    cursor = reader.getCursor()
                    query = 'SELECT userid,objectname FROM _groups_users WHERE userid=%s' % str(u.storage.meta['msId'])
                    cursor.execute(query)
                    buff = cursor.fetchall()
                    comment = buff[0][1]
                    comment = comment[0:comment.find('<br>')]
                    if u.storage.meta['mapShow'] == True and u.storage.meta.has_key('mapComment'):
                        comment += '<br>' + u.storage.meta['mapComment']
                    query = "UPDATE _groups_users SET objectname='%s' WHERE userid=%s" % (comment, str(u.storage.meta['msId']))
                    cursor.execute(query)
                    reader.connection.commit()

                log.info('modified user %s as %s', u.storage.meta['email'], u.hash)
                return ['Изменения успешно внесены']

        return ['Ошибка изменения персональных данных']


    def user_card(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                meta = user.storage.meta
                ret = ['<h2>Профиль пользователя %s</h2>' % (str(meta['email']))]

                ret += ['<div class="row">']
                ret += ['<div class="span8">']

                '''
                ret += ['<ul id="tab" class="nav nav-tabs">']
                ret += ['<li class="active">']
                ret += ['<a href="#BaseParams" data-toggle="tab">Основые параметры</a>']
                ret += ['</li>'] #закрыли таб
                ret += ['<li>']
                ret += ['<a href="#RuntimeData" data-toggle="tab">Оперативная информация</a>']
                ret += ['</li>'] #закрыли таб
                ret += ['</ul>'] #закрыли навигационный объект

                ret += ['\
                        <div id="myTabContent" class="tab-content">\
                            <div class="tab-pane fade active in" id="BaseParams"><h3>XEP1</h3></div>\
                            <div class="tab-pane fade" id="RuntimeData"><h3>XEP2</h3></div>\
                        </div>\
                        ']
                '''

                ret += ['<form class="form-horizontal">']

                statuses = {'A' : ('Карта активна', 'label-success'), 
                            'D' : ('Карта приостановлена', 'label-warning'),
                            'C' : ('Карта закрыта', 'label-important') }
                if meta.has_key('status'): status = statuses.get(meta['status'])
                else: status = statuses.get('D')
                ret += ['<span class="label %s">%s</span>' % (status[1], status[0])]

                ret += ['<fieldset>']
                textbox = w.PatternElement('user_email', 'span', 'Логин')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_email',
                                        'id':'user_email',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       meta['email'], True)
                ret += val

                if meta.has_key('realmail'):
                    textbox = w.PatternElement('user_realmail', 'span', 'Email')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_realmail',
                                            'id':'user_realmail',
                                            'size':'100',
                                            'type':'text',
                                           },
                                           meta['realmail'], True)
                    ret += val




                if meta.has_key('cardtype'):
                    textbox = w.PatternElement('user_cardtype', 'span', 'Тип карты')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_cardtype',
                                            'id':'user_cardtype',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['cardtype'], True)
                    ret += val

                if meta.has_key('realname'):
                    textbox = w.PatternElement('user_realname', 'span', 'ФИО')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_realname',
                                            'id':'user_realname',
                                            'size':'100',
                                            'type':'text',
                                           },
                                           meta['realname'], True)
                    ret += val

                if meta.has_key('phone'):
                    phones = meta['phone']
                    phones.strip()
                    phones = phones.split(';')
                    ctr = 1
                    for num in phones:
                        textbox = w.PatternElement('user_phone' + str(ctr), 'span', 'Номер телефона %s' % (str(ctr)))
                        val     = textbox.open({
                                                'class':'input-xlarge uneditable-input',
                                                'name':'user_phone' + str(ctr),
                                                'id':'user_phone' + str(ctr),
                                                'size':'100',
                                                'type':'text',
                                               },
                                               num, True)
                        ret += val
                        ctr += 1

                if meta.has_key('civility'):
                    textbox = w.PatternElement('user_civility', 'span', 'Гражданство')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_civility',
                                            'id':'user_civility',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['civility'], True)
                    ret += val
                if meta.has_key('startdate'):
                    textbox = w.PatternElement('user_startdate_out', 'span', 'Дата выдачи карты Hot-Line')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_startdate_out',
                                            'id':'user_startdate_out',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['startdate'].strftime('%Y-%m-%d'), True)
                    ret += val
                if meta.has_key('stopdate'):
                    shouldHighlightRed = False
                    if meta['stopdate'] - datetime.datetime.today() < datetime.timedelta(days=30): shouldHighlightRed = True
                    if shouldHighlightRed:
                        ret += '<div class="clearfix">\
                                <label class="span8">Внимание! До окончания срока действия меньше 30и дней</label>\
                                </div>'

                    textbox = w.PatternElement('user_stopdate_out', 'span', 'Дата окончания действия карты Hot-Line')
                    val     = textbox.open({
                                            'class':'input-xlarge success uneditable-input',
                                            'name':'user_stopdate_out',
                                            'id':'user_stopdate_out',
                                            'size':'30',
                                            'type':'text',
                                           },
                                           meta['stopdate'].strftime('%Y-%m-%d'), True)
                    ret += val

                if meta.has_key('alarmHours'):
                    alarmHours = 'НЕВЕРНОЕ ЗНАЧЕНИЕ'
                    try:
                        alarmHours = int(meta['alarmHours'])
                        alarmHours = str(alarmHours)
                    except:
                        pass
                    textbox = w.PatternElement('user_alarmHours', 'span', 'Часов без выхода на связь до начала автоинформирования')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_alarmHours',
                                            'id':'user_alarmHours',
                                            'size':'100',
                                            'type':'text',
                                           },
                                           alarmHours, True)
                    ret += val


                if meta.has_key('extrainfo'):
                    textbox = w.PatternElement('user_extrainfo', 'textarea', 'Дополнительная информация')
                    val     = textbox.open({
                                            'class':'input-xlarge',
                                            'name':'user_extrainfo',
                                            'id':'user_extrainfo',
                                            'size':'30',
                                            'type':'text',
                                            'rows':"6"
                                           },
                                           meta['extrainfo'], True)
                    ret += val

                if meta.has_key('mapShow') and meta['mapShow'] == True:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                            'checked':'checked',
                                            'disabled':'true'
                                           })
                    ret += val
                else:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                            'disabled':'true'
                                           })
                    ret += val


                if meta.has_key('mapComment'):
                    textbox = w.PatternElement('user_mapComment', 'span', 'Комментарий на карте')
                    val     = textbox.open({
                                            'class':'input-xlarge uneditable-input',
                                            'name':'user_mapComment',
                                            'id':'user_mapComment',
                                            'size':'100',
                                            'type':'text',
                                           },
                                           meta['mapComment'], True)
                    ret += val

                textbox = w.PatternElement('user_batteryState', 'span', 'Заряд аккумулятора')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_batteryState',
                                        'id':'user_batteryState',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       'информация недоступна', True)
                ret += val

                textbox = w.PatternElement('user_lastAlarm', 'span', 'Последнее тревожное событие')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_lastAlarm',
                                        'id':'user_lastAlarm',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       'информация недоступна', True)
                ret += val


                ret += ['</fieldset>']
                ret += ['</form>']
                ret += ['</div>'] #закрыли левую область
                ret += ['<div class="span4">']

                ret += ['<div class="clearfix">']
                ret += ['<div id="scale-normal" style="height: 400px">']
                if meta.has_key('photolink'):
                    ret += ['<img src="%s">' % str(meta['photolink'])]
                else:
                    ret += ['<img src="/image/boobs.jpg">']
                ret += ['</div>']
                ret += ['</div>']

                ret += ['</div>'] #закрыли правую область
                ret += ['</div>'] #закрыли весь ряд

                return ret
        return ['please login']

    def user_card_modify(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                meta = user.storage.meta
                ret = ['<h2>Настройки пользователя %s</h2>' % (str(meta['email']))]

                ret += ['<div class="row">']
                if meta.has_key('status') and meta['status'] == 'A':
                    ret += ['<a class="btn" href="/method/user_deactivate">Приостановить карту</a>']
                    ret += ['<a class="btn" href="/method/user_close">Закрыть карту</a>']
                else:
                    ret += ['<a class="btn" href="/method/user_activate">Активировать карту</a>']
                ret += ['<a class="btn" href="/html/password_change.html" target="_blank">Изменить пароль</a>']
                ret += ['</div>']

                ret += ['<div class="row">']
                ret += ['<div class="span8">']
                ret += ['<form action="/method/modify" method="post">']
                ret += ['<fieldset>']
                textbox = w.PatternElement('user_email', 'span', 'Логин')
                val     = textbox.open({
                                        'class':'input-xlarge uneditable-input',
                                        'name':'user_email',
                                        'id':'user_email',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       meta['email'], True)
                ret += val

                ph = ''
                if meta.has_key('cardtype'): ph = meta['cardtype']
                elt =   '<div class="clearfix">'
                elt +=  '<label for="user_cardtype">Тип карты</label>'
                elt +=  '<div class="controls">'
                elt +=  '<select id="user_cardtype" name="user_cardtype">'
                elt +=  '   <option>24часа</option>'
                elt +=  '   <option>рабочие часы</option>'
                elt +=  '   <option>другое</option>'
                elt +=  '</select>'
                elt +=  '<span class="help-inline">* - обязательный параметр</span>'
                elt +=  '</div>'
                elt +=  '</div>'
                '''
                textbox = w.PatternElement('user_cardtype', 'input', 'Тип карты')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_cardtype',
                                        'id':'user_cardtype',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val
                '''
                ret += elt

                ph = ''
                if meta.has_key('realmail'): ph = meta['realmail']
                textbox = w.PatternElement('user_realmail', 'input', 'Email')
                textbox.setHelpInline('* - обязательный параметр')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_realmail',
                                        'id':'user_realmail',
                                        'size':'100',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val


                ph = ''
                if meta.has_key('realname'): ph = meta['realname']
                textbox = w.PatternElement('user_realname', 'input', 'ФИО')
                textbox.setHelpInline('* - обязательный параметр')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_realname',
                                        'id':'user_realname',
                                        'size':'100',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val

                ph = ''
                if meta.has_key('phone'): ph = meta['phone']
                textbox = w.PatternElement('user_phone', 'input', 'Номер телефона')
                textbox.setHelpInline('* - обязательный параметр')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_phone',
                                        'id':'user_phone',
                                        'size':'100',
                                        'type':'text',
                                        'value':ph
                                       }, True)
                ret += val

                ph = ''
                if meta.has_key('civility'): ph = meta['civility']
                textbox = w.PatternElement('user_civility', 'input', 'Гражданство')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_civility',
                                        'id':'user_civility',
                                        'size':'30',
                                        'type':'text',
                                        'value':ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('startdate'): ph = meta['startdate'].strftime('%d.%m.%Y')
                textbox = w.PatternElement('user_startdate', 'input', 'Дата выдачи карты Hot-Line')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_startdate',
                                        'id':'user_startdate',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('stopdate'): ph = meta['stopdate'].strftime('%d.%m.%Y')
                textbox = w.PatternElement('user_stopdate', 'input', 'Дата окончания действия карты Hot-Line')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_stopdate',
                                        'id':'user_stopdate',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('photolink'): ph = meta['photolink']
                textbox = w.PatternElement('user_photolink', 'input', 'Путь к фотографии')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_photolink',
                                        'id':'user_photolink',
                                        'size':'30',
                                        'type':'text',
                                        'value': ph
                                        }, True)
                ret += val

                ph = ''
                if meta.has_key('alarmHours'):
                    try:
                        ph = int(meta['alarmHours'])
                        ph = str(ph)
                    except:
                        ph = 'НЕВЕРНОЕ ЗНАЧЕНИЕ'
                textbox = w.PatternElement('user_alarmHours', 'input', 'Часов без выхода на связь до начала автоинформирования')
                val     = textbox.open({
                                        'class':'span8',
                                        'name':'user_alarmHours',
                                        'id':'user_alarmHours',
                                        'size':'100',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val

                ph = ''
                if meta.has_key('extrainfo'): ph = meta['extrainfo']
                textbox = w.PatternElement('user_extrainfo', 'textarea', 'Дополнительная информация')
                val     = textbox.open({
                                        'class':'input-xlarge',
                                        'name':'user_extrainfo',
                                        'id':'user_extrainfo',
                                        'size':'30',
                                        'type':'text',
                                       },
                                       ph, True)
                ret += val

                if meta.has_key('mapShow') and meta['mapShow'] == True:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                            'checked':'checked',
                                           })
                    ret += val
                else:
                    textbox = w.PatternElement('user_displayAtMap', 'input', 'Отображать на карте')
                    val     = textbox.open({
                                            'name':'user_displayAtMap',
                                            'id':'user_displayAtMap',
                                            'type':'checkbox',
                                           })
                    ret += val

                ph = ''
                if meta.has_key('mapComment'): ph = meta['mapComment']
                textbox = w.PatternElement('user_mapComment', 'input', 'Комментарий на карте')
                val     = textbox.open({
                                        'class':'input-xlarge',
                                        'name':'user_mapComment',
                                        'id':'user_mapComment',
                                        'size':'100',
                                        'maxlength':'100',
                                        'type':'text',
                                        'value': ph
                                       }, True)
                ret += val


                ret += ['</fieldset>']
                ret += ['<button class="btn primary" type="submit">Сохранить изменения</button>']
                ret += ['</form>']
                ret += ['</div>'] #закрыли левую область
                ret += ['</div>'] #закрыли весь ряд

                return ret
        return ['please login']

    def user_activate(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                user.lock.acquire()
                try:
                    user.storage.meta['status'] = 'A'
                    user.flush(True)
                finally:
                    user.lock.release()
                return ['Активирована']
        return ['please login']
    def user_deactivate(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                user.lock.acquire()
                try:
                    user.storage.meta['status'] = 'D'
                    user.flush(True)
                finally:
                    user.lock.release()
                return ['Приостановлена']
        return ['please login']
    def user_close(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                user.lock.acquire()
                try:
                    user.storage.meta['status'] = 'C'
                    user.flush(True)
                finally:
                    user.lock.release()
                return ['Закрыта']
        return ['please login']

    def user_pwd_change(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])

        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        oldpwd  = form.getvalue('user_oldpwd')
        if not Helpers.checkName(oldpwd): return ['Ошибка']
        pwd     = form.getvalue('user_pwd')
        if not Helpers.checkName(pwd): return ['Ошибка']
        pwd2    = form.getvalue('user_pwd2')
        if not Helpers.checkName(pwd2): return ['Ошибка']
        if MSUser.hash(pwd) != MSUser.hash(pwd2): return ['Ошибка - пароли несовпадают']

        if MSUser.isValid(cookie):
            user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
            if user != None and user.validate(cookie):
                if MSUser.hash(oldpwd) == user.storage.meta['pwd']:
                    user.lock.acquire()
                    try:
                        user.storage.meta['pwd'] = MSUser.hash(pwd)
                        user.flush(True)
                    finally:
                        user.lock.release()
                    return ['Успешно изменен']
        return ['Ошибка установки пароля']

    def event_protocol(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        if not MSUser.isValid(cookie): return ['Ошибка доступа']
        user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
        if user == None or (not user.validate(cookie)): return ['Ошибка доступа']

        '''
        we're here when cookie is validated; there're 3 ways to obtain events:
        1. via API - apiSKey is provided
        2. via SQL db - sqldbDeviceId is provided
        3. via MSDatabase (planned) - nothing should be provided
        '''

        reader = None
        if user.storage.meta.has_key('skey'):
            reader = ReaderMSAPI.Reader(c.get('msapi','url'))
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))

        if not reader:
            return ['Ошибка - не предоставлен обязательный параметр']

        startdate   = form.getvalue('obj_startdate')
        if startdate == None:
            startdate = MSDatetime.getToday()[0]
        else:
            startdate = datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG)

        if reader != None:
            obj = MSObject.MSObject(c.get('main', 'dbpath'), 'nonwriteable_object')
            obj.open()
            if obj.storage.txn > 0:
                raise RuntimeError('event_protocol: nonwriteable_object exists!')
            if user.storage.meta.has_key('skey'):
                obj.storage.meta['skey'] = user.storage.meta['skey']
            if user.storage.meta.has_key('msId'):
                obj.storage.meta['msId'] = user.storage.meta['msId']
            reader.getEvents_TIMED(obj, startdate)

        ret = []
        ret += ['<h2>']
        ret += ['Протокол событий пользователя "' + str(user.storage.meta['email']) + '" с ' + startdate.strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG)]
        ret += ['</h2>']

        t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')

        copied_events = list(obj.events)
        copied_events.reverse()
        ret += t.open([('time', {'class':'header'}),
                       ('message',  {'class':'yellow header'}),
                       ('id',   {'class':'yellow header'}),
                       ('comment',   {'class':'blue header'}),
                      ])
        eventnum = 0
        cursor = reader.getCursor()
        for e in copied_events:
            code = MSEvent.eventType(e.contactIdFull, 't') + ' ' + MSEvent.eventCode(e.contactIdFull, 't')
            if code.find('Unknown') != -1: continue

            query   = 'SELECT eventId,comment FROM _comment_data_internal WHERE eventId=%s' % str(e.eventId)
            cursor.execute(query)
            data    = cursor.fetchall()
            if len(data) == 1 and len(data[0][1]) > 0:
                e.eventComment = str(data[0][1])
            else:
                e.eventComment = ''

            ret += t.add((  str(e.eventGenerateTime), 
                            code,
                            str(e.eventId), 
                            str(e.eventComment) ))
            eventnum += 1
        ret += t.close()

        ret += ['Всего событий: <div class="eventTableSize">%d/%d</div><br>' % (eventnum, len(obj.events))]
        ret += ['Время обновления: ']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG) + '</div><br>']
        return ret

    def event_protocol_alarms(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        if not MSUser.isValid(cookie): return ['Ошибка доступа']
        user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
        if user == None or (not user.validate(cookie)): return ['Ошибка доступа']

        '''
        we're here when cookie is validated; there're 3 ways to obtain events:
        1. via API - apiSKey is provided
        2. via SQL db - sqldbDeviceId is provided
        3. via MSDatabase (planned) - nothing should be provided
        '''

        reader = None
        if user.storage.meta.has_key('skey'):
            reader = ReaderMSAPI.Reader(c.get('msapi','url'))
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))

        if not reader:
            return ['Ошибка - не предоставлен обязательный параметр']

        startdate   = form.getvalue('obj_startdate')
        if startdate == None:
            startdate = MSDatetime.getToday()[0]
        else:
            startdate = datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG)

        if reader != None:
            obj = MSObject.MSObject(c.get('main', 'dbpath'), 'nonwriteable_object')
            obj.open()
            if obj.storage.txn > 0:
                raise RuntimeError('event_protocol: nonwriteable_object exists!')
            if user.storage.meta.has_key('skey'):
                obj.storage.meta['skey'] = user.storage.meta['skey']
            if user.storage.meta.has_key('msId'):
                obj.storage.meta['msId'] = user.storage.meta['msId']
            reader.getEvents_TIMED(obj, startdate)

        ret = []
        ret += ['<h2>']
        ret += ['Протокол событий пользователя "' + str(user.storage.meta['email']) + '" с ' + startdate.strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG)]
        ret += ['</h2>']

        t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')

        copied_events = list(obj.events)
        copied_events.reverse()
        ret += t.open([('time', {'class':'header'}),
                       ('message',  {'class':'yellow header'}),
                       ('id',   {'class':'yellow header'}),
                       ('comment',   {'class':'blue header'}),
                      ])
        eventnum = 0
        cursor = reader.getCursor()
        for e in copied_events:
            codes = {85:'yes',
                     88:'yes',
                     293:'yes'
                    }
            if codes.get(int(e.pgsmType), None) == None: continue
            code = MSEvent.eventType(e.contactIdFull, 't') + ' ' + MSEvent.eventCode(e.contactIdFull, 't')
            if code.find('Unknown') != -1: continue

            query   = 'SELECT eventId,comment FROM _comment_data_internal WHERE eventId=%s' % str(e.eventId)
            cursor.execute(query)
            data    = cursor.fetchall()
            if len(data) == 1 and len(data[0][1]) > 0:
                e.eventComment = str(data[0][1])
            else:
                e.eventComment = ''

            ret += t.add((  str(e.eventGenerateTime), 
                            code,
                            str(e.eventId), 
                            str(e.eventComment) ))
            eventnum += 1
        ret += t.close()

        ret += ['Всего событий: <div class="eventTableSize">%d/%d</div><br>' % (eventnum, len(obj.events))]
        ret += ['Время обновления: ']
        ret += ['<div class="eventTableUpdTime">' + (datetime.datetime.fromtimestamp(time.time())).strftime(MSDatetime.TIME_FORMAT_MAGICDB_ORIG) + '</div><br>']
        return ret


    def location_static(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form        = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)

        if not MSUser.isValid(cookie): return ['Ошибка доступа']
        user = MSDatabase.getInstance().users.find(str(cookie['id'].value))
        if user == None or (not user.validate(cookie)): return ['Ошибка доступа']

        '''
        we're here when cookie is validated; there're 3 ways to obtain events:
        1. via API - apiSKey is provided
        2. via SQL db - sqldbDeviceId is provided
        3. via MSDatabase (planned) - nothing should be provided
        '''
        reader = None
        if user.storage.meta.has_key('skey'):
            reader = ReaderMSAPI.Reader(c.get('msapi','url'))
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))
        if not reader:
            return ['Ошибка - не предоставлен обязательный параметр']

        timerange = MSDatetime.getToday()
        startdate   = form.getvalue('obj_startdate')
        if startdate != None:
            timerange = (datetime.datetime.strptime(startdate, MSDatetime.TIME_FORMAT_MAGICDB_ORIG), timerange[1])

        if reader != None:
            obj = MSObject.MSObject(c.get('main', 'dbpath'), 'nonwriteable_object')
            obj.open()
            if obj.storage.txn > 0:
                raise RuntimeError('event_protocol: nonwriteable_object exists!')
            if user.storage.meta.has_key('skey'):
                obj.storage.meta['skey'] = user.storage.meta['skey']
            if user.storage.meta.has_key('msId'):
                obj.storage.meta['msId'] = user.storage.meta['msId']
            reader.getEvents_GPS_TIMED(obj, reader.normalizeTimerange(timerange))

        ret = []
        ret += ['<h2>']
        ret += ['Местоположение пользователя "' + str(user.storage.meta['email']) + '"']
        ret += ['</h2>']

        copied_events = list(obj.events)
        if len(copied_events) == 0:
            ret += ['нет данных']
            return ret

        ret += ['<script type="text/javascript">']
        ret += ['\n\
                function mapMobileObjects(map) {\n\
                    var blueIcon = new GIcon(G_DEFAULT_ICON);\n\
                    blueIcon.iconSize = new GSize(45,30);\n\
                    blueIcon.image = "http://extras.inyork.com/redesign2010/images/autoIcon.png";\n\
                    markerOptions = { icon:blueIcon };\n\
        ']
        last = copied_events[0]
        ret += ['map.addOverlay(new GMarker(new GLatLng(%s, %s), markerOptions));\n' % (last.GPSLatitudeOrig, last.GPSLongitudeOrig)]
        ret += ['}']

        ret += ['\
                function initialize() {\n\
                  if (GBrowserIsCompatible()) {\n\
                    var map = new GMap2(document.getElementById("map_canvas"));\n\
                    map.setCenter(new GLatLng(59.986379, 30.248780), 10);\n\
                    map.setUIToDefault();\n\
                    mapMobileObjects(map);\n\
                    //var trafficOptions = {incidents:true};\n\
                    //trafficInfo = new GTrafficOverlay(trafficOptions);\n\
                    //map.addOverlay(trafficInfo);\n\
                  }\n\
                }\n']
        ret += ['</script>']
        ret += ['<div id="map_canvas" style="width: 1000px; height: 600px"></div>']
        return ret

    def comment_alarm(self, environ, start_response, cookie):
        w.Common.putHeader(start_response, 200, [('html', 'text')])
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        eventid = form.getvalue('event_id')
        if not eventid:
            return ['Ошибка доступа - не указан номер события']
        try:
            eventid = int(eventid)
        except:
            return ['Ошибка доступа - неверный номер события']

        reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                   c.get('msdb','port'),
                                   c.get('msdb','dbname'),
                                   c.get('msdb','user'),
                                   c.get('msdb','pwd'))
        if not reader:
            return ['Ошибка - пользователь еще не зарегистрирован - нельзя обрабатывать тревоги по нему']

        comment = form.getvalue('event_comment')
        if not comment or len(comment) == 0:
            return ['Ошибка - не задан комментарий']

        cursor  = reader.getCursor()
        query   = 'DELETE FROM _comment_data_internal WHERE eventId=%s' % (str(eventid))
        cursor.execute(query)
        query   = "INSERT INTO _comment_data_internal (eventId, comment) VALUES(%s,'%s')" % (str(eventid), str(comment))
        cursor.execute(query)
        reader.connection.commit()
        return ['Комментарий успешно обновлен']

    def process_alarm_form(self, environ, start_response, cookie):
        loginInfo = self.login(environ, start_response, cookie)
        if len(loginInfo) == 0 or loginInfo[0].find('Выполнен вход') == -1:
            return ['Ошибка доступа - неверный логин']
        form    = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ)
        eventid = form.getvalue('eventId')
        if not eventid:
            return ['Ошибка доступа - не указан номер события']
        try:
            eventid = int(eventid)
        except:
            return ['Ошибка доступа - неверный номер события']

        user = MSDatabase.getInstance().users.find(MSUser.hash(form.getvalue('login')))
        reader = None
        if user.storage.meta.has_key('msId'):
            reader = ReaderMSDB.Reader(c.get('msdb','host'),
                                       c.get('msdb','port'),
                                       c.get('msdb','dbname'),
                                       c.get('msdb','user'),
                                       c.get('msdb','pwd'))
        if not reader:
            return ['Ошибка - пользователь еще не зарегистрирован - нельзя обрабатывать тревоги по нему']
        cursor  = reader.getCursor()
        query   = 'SELECT deviceId,eventTime,type,id FROM _event_data_internal WHERE id=%s' % str(eventid)
        cursor.execute(query)
        data    = cursor.fetchall()
        events  = ReaderMSDB.createEvents(data, 0)
        if len(events) != 1:
            return ['Ошибка - искомое событие не найдено в базе']
        e       = events[0]

        query   = 'SELECT eventId,comment FROM _comment_data_internal WHERE eventId=%s' % str(eventid)
        cursor.execute(query)
        data    = cursor.fetchall()
        if len(data) == 1 and len(data[0][1]) > 0:
            e.eventComment = str(data[0][1])
        else:
            e.eventComment = ''

        ret = ['<h2>Обработка тревоги %s</h2>' % (str(eventid))]
        t = w.PatternTable('zebra-striped', 'eventTableSortable', 'eventTableBody')
        ret += t.open([('time', {'class':'header'}),
                       ('message',  {'class':'yellow header'}),
                       ('id',   {'class':'yellow header'}),
                       ('comment', {'class':'blue header'}),
                      ])
        code = MSEvent.eventType(e.contactIdFull, 't') + ' ' + MSEvent.eventCode(e.contactIdFull, 't')
        ret += t.add(( str(e.eventGenerateTime),
                       code,
                       str(e.eventId),
                       str(e.eventComment) ))
        ret += t.close()

        ret += ['<div class="row">']
        ret += ['<div class="span8">']
        ret += ['<form action="/method/comment_alarm" method="get">']
        ret += ['<fieldset>']

        textbox = w.PatternElement('event_id', 'input', 'Номер события')
        val     = textbox.open({
                                'class':'input-xlarge uneditable-input',
                                'name':'event_id',
                                'id':'event_id',
                                'size':'30',
                                'type':'text',
                                'value':str(eventid),
                               },
                               True)
        ret += val

        textbox = w.PatternElement('event_comment', 'textarea', 'Комментарий к тревоге')
        val     = textbox.open({
                                'class':'input-xlarge',
                                'name':'event_comment',
                                'id':'event_comment',
                                'size':'30',
                                'type':'text',
                               },
                               e.eventComment, True)
        ret += val
        ret += ['</fieldset>']
        ret += ['<button class="btn primary" type="submit">Сохранить комментарий</button>']
        ret += ['</form>']
        ret += ['</div>'] #закрыли левую область
        ret += ['</div>'] #закрыли весь ряд

        return ret
